﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Xml;
using SHomeWorkshop.LunarConcept.Adorners;
using SHomeWorkshop.LunarConcept.Controls;
using SHomeWorkshop.LunarConcept.Enums;
using SHomeWorkshop.LunarConcept.Tools;
using SHomeWorkshop.LunarConcept.Widgets.Interfaces;
using System.Collections.Generic;
using SHomeWorkshop.LunarMind;

namespace SHomeWorkshop.LunarConcept.Widgets
{
    /// <summary>
    /// 创建时间：2012年1月20日
    /// 创建者：  杨震宇
    /// 
    /// 主要用途：三角形部件。
    /// </summary>
    public class TriangleWidget : ShapeWidget, ICanBeLinkedWidget, Widgets.Interfaces.ITextRotate
    {
        #region 构造方法=====================================================================================================

        /// <summary>
        /// [静态构造方法]
        /// </summary>
        static TriangleWidget()
        {
            dashArray = new DoubleCollection() { 2, 2 };
        }

        /// <summary>
        /// [构造方法]
        /// </summary>
        public TriangleWidget(PageEditorReader masterEditor)
            : base(masterEditor)
        {
            widgetType = Enums.WidgetTypes.Triangle;
            widgetClassLocalName = Widget.GetWidgetClassLocalName(this.GetType().Name);

            this.mainPath.Data = this.pathGeometry;
            this.pathGeometry.Figures.Add(this.pathFigure);
            this.pathFigure.Segments.Add(this.arcSegmentAtInner);
            this.pathFigure.Segments.Add(this.startLineSegment);
            this.pathFigure.Segments.Add(this.arcSegmentAtOuter);
            this.pathFigure.Segments.Add(this.endLineSegment);

            //mainPolygon.Fill = null;
            //mainPolygon.Stroke = WidgetForeColor;
            mainPath.Fill = null;
            mainPath.Stroke = WidgetForeColor;

            this.mainCanvas.Children.Add(mainPath);

            Canvas.SetZIndex(mainPath, 0);

            this.commentAdorner = new CommentAdorner(this.mainPath, this) { Visibility = System.Windows.Visibility.Collapsed };//默认不显示。
            this.hyperLinkAdorner = new HyperLinkAdorner(this.mainPath, this) { Visibility = Visibility.Collapsed };

            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this.mainCanvas);
            if (adornerLayer == null)
            {
                MessageBox.Show("　　未找到Widget的装饰层！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
            else
            {
                adornerLayer.Add(this.commentAdorner);//这个要在各具体部件类中添加。
                adornerLayer.Add(this.hyperLinkAdorner);//这个要在各具体部件类中添加。
            }

            //最后添加文本面板。
            this.mainCanvas.Children.Add(this.mainTextPanel);
            Canvas.SetZIndex(this.mainTextPanel, 2);
        }

        #endregion


        #region 字段与属性===================================================================================================

        public override Point BottomRight
        {
            get
            {
                double minLeft, minTop, maxRight, maxBottom;

                minLeft = Math.Min(Math.Min(startPoint.X, endPoint.X), centerCPPoint.X);
                maxRight = Math.Max(Math.Max(startPoint.X, endPoint.X), centerCPPoint.X);

                minTop = Math.Min(Math.Min(startPoint.Y, endPoint.Y), centerCPPoint.Y);
                maxBottom = Math.Max(Math.Max(startPoint.Y, endPoint.Y), centerCPPoint.Y);

                return new Point(maxRight, maxBottom);
            }
        }

        private Point centerCPPoint = new Point();
        [Tools.LunarProperty("CenterCPPoint", PropertyDateType.Point)]
        public Point CenterCPPoint
        {
            get { return centerCPPoint; }
            set
            {
                centerCPPoint = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.CenterCPPointTag, value.ToString());
                }

                RefreshLocation();
            }
        }

        private Point endPoint = new Point();
        /// <summary>
        /// [读写]线的终点坐标。
        /// </summary>
        [Tools.LunarProperty("EndPoint", PropertyDateType.Point)]
        public Point EndPoint
        {
            get { return endPoint; }
            set
            {
                endPoint = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.EndPointTag, value.ToString());
                }

                RefreshLocation();
            }
        }

        /// <summary>
        /// [只读]表示当前部件是否被某个连接线挂接着。
        /// </summary>
        public bool IsLinked
        {
            get
            {
                if (this.masterEditor == null) return false;

                foreach (UIElement ue in this.masterEditor.Children)
                {
                    ILinkableLine linkedLine = ue as ILinkableLine;
                    if (linkedLine == null) continue;

                    if (linkedLine.StartMasterId == this.id || linkedLine.EndMasterId == this.id) return true;
                }

                return false;
            }
        }
        
        #region 画三角形、饼形、环形等

        private Path mainPath = new Path() { Cursor = Cursors.Hand, StrokeEndLineCap = PenLineCap.Flat, };

        private PathGeometry pathGeometry = new PathGeometry();

        private PathFigure pathFigure = new PathFigure();

        private LineSegment startLineSegment = new LineSegment();

        private LineSegment endLineSegment = new LineSegment();

        /// <summary>
        /// 三角形SE边由弧来实现。
        /// </summary>
        private ArcSegment arcSegmentAtOuter = new ArcSegment();

        private ArcSegment arcSegmentAtInner = new ArcSegment();

        //2019年11月6日。专门用于新增的“花括弧模式”——此花括弧与 Bracket 部件不同——可以支持斜向。
        private BezierSegment startBezierSegment = new BezierSegment();

        private BezierSegment endBezierSegment = new BezierSegment();

        //2019年11月7日。新增T型线。
        private PolyLineSegment polyLineSegment = new PolyLineSegment();

        #endregion

        private Rect movingRect;
        /// <summary>
        /// 部件正在被拖动时的外边框。
        /// </summary>
        public Rect MovingRect
        {
            get
            {
                return movingRect;
            }
        }

        private Point startPoint = new Point(0, 0);
        /// <summary>
        /// [读写]线的起点坐标。
        /// </summary>
        [Tools.LunarProperty("StartPoint", PropertyDateType.Point)]
        public Point StartPoint
        {
            get { return startPoint; }
            set
            {
                startPoint = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.StartPointTag, startPoint.ToString());
                }

                RefreshLocation();
            }
        }

        private double textRotateAngleAuto = 0;

        private double textRotateAngle = 0;
        /// <summary>
        /// [读写]文本旋转角度。取值范围：[-180,180]。
        /// </summary>
        [Tools.LunarProperty("TextRotateAngle", PropertyDateType.Double)]
        public double TextRotateAngle
        {
            get { return textRotateAngle; }
            set
            {
                if (value > 180)
                {
                    textRotateAngle = 180;
                }
                else if (value < -180)
                {
                    textRotateAngle = -180;
                }
                else
                {
                    textRotateAngle = value;
                }

                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.TextRotateAngleTag, textRotateAngle.ToString());
                }

                this.RefreshTextRotateAngle();
            }
        }

        private Enums.TriangleForm triangleForm = TriangleForm.Triangle;
        /// <summary>
        /// [读写]三角形形态。
        /// </summary>
        [Tools.LunarProperty("TriangleForm", PropertyDateType.TriangleForm)]
        public Enums.TriangleForm TriangleForm
        {
            get { return triangleForm; }
            set
            {
                triangleForm = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.TriangleFormTag, triangleForm.ToString());
                }
                DrawLine();
                RefreshTextPanelLocatin();
                RefreshTextRotateAngle();
            }
        }

        public override Point TopLeft
        {
            get
            {
                double minLeft, minTop, maxRight, maxBottom;

                minLeft = Math.Min(Math.Min(startPoint.X, endPoint.X), centerCPPoint.X);
                maxRight = Math.Max(Math.Max(startPoint.X, endPoint.X), centerCPPoint.X);

                minTop = Math.Min(Math.Min(startPoint.Y, endPoint.Y), centerCPPoint.Y);
                maxBottom = Math.Max(Math.Max(startPoint.Y, endPoint.Y), centerCPPoint.Y);

                return new Point(minLeft, minTop);
            }
        }

        #endregion


        #region 方法=========================================================================================================

        public override void Build()
        {
            base.Build();

            if (this.xmlData == null) return;

            XmlAttribute attrStartPoint = this.xmlData.GetAttribute(XmlTags.StartPointTag);
            if (attrStartPoint != null)
            {
                this.startPoint = Point.Parse(attrStartPoint.Value);
            }

            XmlAttribute attrCenterCPPoint = this.xmlData.GetAttribute(XmlTags.CenterCPPointTag);
            if (attrCenterCPPoint != null)
            {
                this.centerCPPoint = Point.Parse(attrCenterCPPoint.Value);
            }

            XmlAttribute attrEndPoint = this.xmlData.GetAttribute(XmlTags.EndPointTag);
            if (attrEndPoint != null)
            {
                this.endPoint = Point.Parse(attrEndPoint.Value);
            }

            XmlAttribute attrTextRotateAngle = this.xmlData.GetAttribute(XmlTags.TextRotateAngleTag);
            if (attrTextRotateAngle != null)
            {
                this.textRotateAngle = double.Parse(attrTextRotateAngle.Value);
            }

            XmlAttribute attrTriangleForm = this.xmlData.GetAttribute(XmlTags.TriangleFormTag);
            if (attrTriangleForm != null)
            {
                this.triangleForm = (Enums.TriangleForm)Enum.Parse(typeof(Enums.TriangleForm), attrTriangleForm.Value);
            }

            this.RefreshTextRotateAngle();
            this.RefreshLocation();

            //此类是下面这几个属性的“最终实现类”。这些属性的值都已在基类确定。因此调用,
            this.RefreshWidgetBackColor();
            this.RefreshWidgetLineColor();
            this.RefreshWidgetLineWidth();
            this.RefreshLineDash();
        }

        public void DrawLine()
        {
            if (masterEditor == null) return;

            DrawLine(this.startPoint, this.centerCPPoint, this.endPoint);
        }

        /// <summary>
        /// 三角形由三个顶点决定。
        /// </summary>
        /// <param name="startPoint">首端点。</param>
        /// <param name="centerCPPoint">中控制点。</param>
        /// <param name="endPoint">尾端点。</param>
        private void DrawLine(Point startPoint, Point centerCPPoint, Point endPoint)
        {
            Point startBasePt = new Point();
            Point endBasePt = new Point();

            startBasePt.X = Math.Min(Math.Min(startPoint.X, centerCPPoint.X), endPoint.X);
            startBasePt.Y = Math.Min(Math.Min(startPoint.Y, centerCPPoint.Y), endPoint.Y);

            endBasePt.X = Math.Max(Math.Max(startPoint.X, centerCPPoint.X), endPoint.X);
            endBasePt.Y = Math.Max(Math.Max(startPoint.Y, centerCPPoint.Y), endPoint.Y);

            //mainPolygon.Points = new PointCollection()
            //{
            //    startPoint,centerCPPoint,endPoint,startPoint,
            //};
            this.pathGeometry.Figures.Clear();
            this.pathGeometry.Figures.Add(this.pathFigure);  // 这是因为思考框有不止一个 PathFigure

            this.pathFigure.Segments.Clear();
            this.pathFigure.IsClosed = true;
            switch (this.triangleForm)
            {
                case Enums.TriangleForm.Triangle:
                    {
                        #region 三角形绘制代码
                        this.pathFigure.Segments.Add(this.arcSegmentAtInner);
                        this.pathFigure.Segments.Add(this.startLineSegment);
                        this.pathFigure.Segments.Add(this.arcSegmentAtOuter);
                        this.pathFigure.Segments.Add(this.endLineSegment);
                        Size zeroSize = new Size(0, 0);

                        this.pathFigure.StartPoint = centerCPPoint;
                        this.arcSegmentAtInner.Point = centerCPPoint;//此时不显示内弧。
                        this.startLineSegment.Point = startPoint;
                        this.arcSegmentAtOuter.Point = endPoint;
                        this.arcSegmentAtOuter.Size = zeroSize;
                        this.endLineSegment.Point = centerCPPoint;
                        #endregion

                        LocateTextPanel(startPoint, centerCPPoint, endPoint);
                        break;
                    }
                case Enums.TriangleForm.Pie:
                    {
                        Point? c = null;
                        #region 饼形绘制代码
                        this.pathFigure.Segments.Add(this.arcSegmentAtInner);
                        this.pathFigure.Segments.Add(this.startLineSegment);
                        this.pathFigure.Segments.Add(this.arcSegmentAtOuter);
                        this.pathFigure.Segments.Add(this.endLineSegment);
                        //饼形，总是取“StartPoint到CenterPoint”与“EndPoint到CenterPoint”这两个线段间长度较大的那个为半径。
                        //以CenterPoint为顶点。总是顺时针方向绘制。

                        double rSToC, rEToC, r;
                        double sXOffsetToC = startPoint.X - centerCPPoint.X;
                        double sYOffsetToC = startPoint.Y - centerCPPoint.Y;
                        rSToC = Math.Sqrt(sXOffsetToC * sXOffsetToC + sYOffsetToC * sYOffsetToC);
                        double eXOffsetToC = endPoint.X - centerCPPoint.X;
                        double eYOffsetToC = endPoint.Y - centerCPPoint.Y;
                        rEToC = Math.Sqrt(eXOffsetToC * eXOffsetToC + eYOffsetToC * eYOffsetToC);

                        Point realStartPt; Point realEndPt;
                        if (rSToC > 0 && rEToC > 0)
                        {
                            this.mainPath.Visibility = System.Windows.Visibility.Visible;
                            if (rSToC > rEToC)
                            {
                                r = rSToC;
                                realStartPt = startPoint;

                                double k = rSToC / rEToC;
                                double x = centerCPPoint.X + (endPoint.X - centerCPPoint.X) * k;
                                double y = centerCPPoint.Y + (endPoint.Y - centerCPPoint.Y) * k;
                                realEndPt = new Point(x, y);

                                c = new Point(realStartPt.X + (realEndPt.X - realStartPt.X) / 2,
                                       realStartPt.Y + (realEndPt.Y - realStartPt.Y) / 2);
                            }
                            else
                            {
                                r = rEToC;
                                realEndPt = endPoint;

                                double k = rEToC / rSToC;
                                double x = centerCPPoint.X + (startPoint.X - centerCPPoint.X) * k;
                                double y = centerCPPoint.Y + (startPoint.Y - centerCPPoint.Y) * k;
                                realStartPt = new Point(x, y);

                                c = new Point(realStartPt.X + (realEndPt.X - realStartPt.X) / 2,
                                       realStartPt.Y + (realEndPt.Y - realStartPt.Y) / 2);
                            }
                            Size zeroSize = new Size(0, 0);
                            this.pathFigure.StartPoint = centerCPPoint;
                            this.arcSegmentAtInner.Size = zeroSize;
                            this.arcSegmentAtInner.Point = centerCPPoint;
                            this.startLineSegment.Point = realStartPt;
                            this.arcSegmentAtOuter.Size = new Size(r, r);
                            this.arcSegmentAtOuter.SweepDirection = SweepDirection.Clockwise;
                            this.arcSegmentAtOuter.IsLargeArc = LargeArc(startPoint, centerCPPoint, endPoint);
                            this.arcSegmentAtOuter.Point = realEndPt;
                            this.endLineSegment.Point = centerCPPoint;
                        }
                        else
                        {
                            this.mainPath.Visibility = System.Windows.Visibility.Hidden;
                        }

                        #endregion

                        if (c != null && c.HasValue)
                        {
                            Canvas.SetLeft(this.mainTextPanel, c.Value.X - this.mainTextPanel.ActualWidth / 2);
                            Canvas.SetTop(this.mainTextPanel, c.Value.Y - this.mainTextPanel.ActualHeight / 2);
                        }

                        break;
                    }
                case Enums.TriangleForm.Sector:
                    {
                        Point? c = null;

                        #region 环形绘制代码
                        this.pathFigure.Segments.Add(this.arcSegmentAtInner);
                        this.pathFigure.Segments.Add(this.startLineSegment);
                        this.pathFigure.Segments.Add(this.arcSegmentAtOuter);
                        this.pathFigure.Segments.Add(this.endLineSegment);
                        //环形，总是取“StartPoint到CenterPoint”与“EndPoint到CenterPoint”这两个线段间长度间距为环径。
                        //以CenterPoint为顶点。总是顺时针方向绘制。

                        double rSToC, rEToC, outerR, innerR;

                        double sXOffsetToC = startPoint.X - centerCPPoint.X;
                        double sYOffsetToC = startPoint.Y - centerCPPoint.Y;
                        rSToC = Math.Sqrt(sXOffsetToC * sXOffsetToC + sYOffsetToC * sYOffsetToC);

                        double eXOffsetToC = endPoint.X - centerCPPoint.X;
                        double eYOffsetToC = endPoint.Y - centerCPPoint.Y;
                        rEToC = Math.Sqrt(eXOffsetToC * eXOffsetToC + eYOffsetToC * eYOffsetToC);

                        Point outerStartPt, outerEndPt, innerStartPt, innerEndPt;
                        if (rSToC > 0 && rEToC > 0)
                        {
                            this.mainPath.Visibility = System.Windows.Visibility.Visible;
                            if (rSToC > rEToC)
                            {
                                outerR = rSToC;
                                innerR = rEToC;
                                outerStartPt = startPoint;

                                double ke = rSToC / rEToC;
                                double xe = centerCPPoint.X + (endPoint.X - centerCPPoint.X) * ke;
                                double ye = centerCPPoint.Y + (endPoint.Y - centerCPPoint.Y) * ke;
                                outerEndPt = new Point(xe, ye);

                                double ks = rEToC / rSToC;
                                double xs = centerCPPoint.X + (startPoint.X - centerCPPoint.X) * ks;
                                double ys = centerCPPoint.Y + (startPoint.Y - centerCPPoint.Y) * ks;
                                innerStartPt = new Point(xs, ys);

                                innerEndPt = endPoint;

                                c = new Point(innerStartPt.X + (innerEndPt.X - innerStartPt.X) / 2,
                                    innerStartPt.Y + (innerEndPt.Y - innerStartPt.Y) / 2);
                            }
                            else
                            {
                                outerR = rEToC;
                                innerR = rSToC;
                                outerEndPt = endPoint;

                                double ks = rEToC / rSToC;
                                double xs = centerCPPoint.X + (startPoint.X - centerCPPoint.X) * ks;
                                double ys = centerCPPoint.Y + (startPoint.Y - centerCPPoint.Y) * ks;
                                outerStartPt = new Point(xs, ys);

                                double ke = rSToC / rEToC;
                                double xe = centerCPPoint.X + (endPoint.X - centerCPPoint.X) * ke;
                                double ye = centerCPPoint.Y + (endPoint.Y - centerCPPoint.Y) * ke;
                                innerEndPt = new Point(xe, ye);

                                innerStartPt = startPoint;

                                c = new Point(innerStartPt.X + (innerEndPt.X - innerStartPt.X) / 2,
                                    innerStartPt.Y + (innerEndPt.Y - innerStartPt.Y) / 2);
                            }

                            bool isLarge = LargeArc(outerStartPt, centerCPPoint, outerEndPt);

                            this.pathFigure.StartPoint = innerEndPt;

                            this.arcSegmentAtInner.Size = new Size(innerR, innerR);
                            this.arcSegmentAtInner.SweepDirection = SweepDirection.Counterclockwise;
                            this.arcSegmentAtInner.IsLargeArc = isLarge;
                            this.arcSegmentAtInner.Point = innerStartPt;

                            this.startLineSegment.Point = outerStartPt;

                            this.arcSegmentAtOuter.Size = new Size(outerR, outerR);
                            this.arcSegmentAtOuter.SweepDirection = SweepDirection.Clockwise;
                            this.arcSegmentAtOuter.IsLargeArc = isLarge;
                            this.arcSegmentAtOuter.Point = outerEndPt;

                            this.endLineSegment.Point = innerEndPt;
                        }
                        else
                        {
                            this.mainPath.Visibility = System.Windows.Visibility.Hidden;
                        }

                        #endregion

                        if (c != null && c.HasValue)
                        {
                            Canvas.SetLeft(this.mainTextPanel, c.Value.X - this.mainTextPanel.ActualWidth / 2);
                            Canvas.SetTop(this.mainTextPanel, c.Value.Y - this.mainTextPanel.ActualHeight / 2);
                        }

                        break;
                    }
                case TriangleForm.Bracket:
                    {
                        #region 绘制可斜向的花括号

                        if (GetBezierControlPoints(startPoint, centerCPPoint, endPoint, out Point? point1, out Point? point2, out Point? point3))
                        {
                            this.pathFigure.Segments.Add(startBezierSegment);
                            this.pathFigure.Segments.Add(endBezierSegment);

                            this.pathFigure.StartPoint = startPoint;
                            startBezierSegment.Point1 = point1.Value;
                            startBezierSegment.Point2 = point2.Value;
                            startBezierSegment.Point3 = centerCPPoint;

                            endBezierSegment.Point1 = point2.Value;
                            endBezierSegment.Point2 = point3.Value;
                            endBezierSegment.Point3 = endPoint;

                            this.pathFigure.IsClosed = false;

                            LocateTextPanel(startPoint, centerCPPoint, endPoint);
                        }
                        #endregion
                        break;
                    }
                case TriangleForm.TLine:
                    {
                        //绘制首尾点连线和中控点向首尾点连线的垂线。
                        if (GetTPoint(startPoint, centerCPPoint, endPoint, out Point? tPoint))
                        {
                            polyLineSegment.Points.Clear();
                            this.pathFigure.Segments.Add(polyLineSegment);

                            this.pathFigure.StartPoint = startPoint;
                            this.polyLineSegment.Points.Add(tPoint.Value);
                            this.polyLineSegment.Points.Add(centerCPPoint);
                            this.polyLineSegment.Points.Add(tPoint.Value);
                            this.polyLineSegment.Points.Add(endPoint);

                            this.pathFigure.IsClosed = false;

                            LocateTextPanel(startPoint, centerCPPoint, endPoint);
                        }
                        break;
                    }
                case TriangleForm.OrgLine:
                    {
                        //绘制首尾点连线和中控点向首尾点连线的垂线。
                        if (GetOrgPoints(startPoint, centerCPPoint, endPoint, out Point? point1, out Point? point2, out Point? point3))
                        {
                            polyLineSegment.Points.Clear();
                            this.pathFigure.Segments.Add(polyLineSegment);

                            this.pathFigure.StartPoint = startPoint;
                            this.polyLineSegment.Points.Add(point1.Value);
                            this.polyLineSegment.Points.Add(point2.Value);
                            this.polyLineSegment.Points.Add(centerCPPoint);
                            this.polyLineSegment.Points.Add(point2.Value);
                            this.polyLineSegment.Points.Add(point3.Value);
                            this.polyLineSegment.Points.Add(endPoint);

                            this.pathFigure.IsClosed = false;

                            LocateTextPanel(startPoint, centerCPPoint, endPoint);
                        }
                        break;
                    }
                case TriangleForm.YLine:
                    {
                        //绘制首尾点连线和中控点向首尾点连线的垂线。
                        if (GetYPoint(startPoint, centerCPPoint, endPoint, out Point? yPoint))
                        {
                            polyLineSegment.Points.Clear();
                            this.pathFigure.Segments.Add(polyLineSegment);

                            this.pathFigure.StartPoint = startPoint;
                            this.polyLineSegment.Points.Add(yPoint.Value);
                            this.polyLineSegment.Points.Add(centerCPPoint);
                            this.polyLineSegment.Points.Add(yPoint.Value);
                            this.polyLineSegment.Points.Add(endPoint);

                            this.pathFigure.IsClosed = false;

                            LocateTextPanel(startPoint, centerCPPoint, endPoint);
                        }
                        break;
                    }
                case TriangleForm.DialogBubble:
                    {
                        //绘制对话框气泡——这种与矩形部件的气泡相比，更灵活，可以移动控制点，行为更像PPT里的圆角矩形气泡框。
                        DrawDialogBubble(startPoint, centerCPPoint, endPoint);

                        LocateTextPanel(startPoint, centerCPPoint, endPoint);
                        break;
                    }
                case TriangleForm.ThinkingBubble:
                    {
                        //绘制对话框气泡——这种与矩形部件的气泡相比，更灵活，可以移动控制点，行为更像PPT里的圆角矩形气泡框。
                        DrawThinkingCloud(startPoint, centerCPPoint, endPoint);

                        LocateTextPanel(startPoint, centerCPPoint, endPoint);
                        break;
                    }
            }

            //移动部件控制点时，要刷新连接线的。
            this.movingRect = new Rect(startBasePt, endBasePt);
        }

        /// <summary>
        /// 这种与矩形部件的气泡相比，更灵活，可以移动控制点，行为更像PPT里的圆角矩形气泡框。
        /// ptStart/ptEnd定义一个圆角矩形，ptCPPoint决定了尖角的相对位置。
        /// </summary>
        /// <param name="ptStart"></param>
        /// <param name="ptCPPoint"></param>
        /// <param name="ptEnd"></param>
        private void DrawDialogBubble(Point ptStart, Point ptCPPoint, Point ptEnd)
        {
            var rect = new Rect(ptStart, ptEnd);
            var ptCenter = new Point(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2);
            var w = rect.Width / 80;
            var h = rect.Height / 80;

            // var radiu = Math.Min(rect.Width, rect.Height) / 8;
            var radiu = Math.Min(h * 12, w * 12);

            var direct = GetPointArea(ptCPPoint, rect);
            switch (direct)
            {
                case "aa":
                case "a1":  // 尖角在矩形顶部左侧
                    {
                        this.pathFigure.Segments.Clear();
                        this.pathFigure.StartPoint = ptCPPoint;
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(ptCenter.X - 8 * w, rect.Top), });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right - radiu, rect.Top), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right, rect.Top + radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right, rect.Bottom - radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right - radiu, rect.Bottom), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left + radiu, rect.Bottom), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left, rect.Bottom - radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left, rect.Top + radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left + radiu, rect.Top), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left + 13 * w, rect.Top), });
                        this.pathFigure.IsClosed = true;
                        break;
                    }
                case "a2":
                case "ab":   // 尖角在矩形顶部右侧
                    {
                        this.pathFigure.Segments.Clear();
                        this.pathFigure.StartPoint = ptCPPoint;
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right - 13 * w, rect.Top), });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right - radiu, rect.Top), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right, rect.Top + radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right, rect.Bottom - radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right - radiu, rect.Bottom), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left + radiu, rect.Bottom), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left, rect.Bottom - radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left, rect.Top + radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left + radiu, rect.Top), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(ptCenter.X + 13 * w, rect.Top), });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = ptCPPoint, });
                        this.pathFigure.IsClosed = true;
                        break;
                    }
                case "ac":
                case "a3":   // 尖角在矩形右侧偏上
                    {
                        this.pathFigure.Segments.Clear();
                        this.pathFigure.StartPoint = ptCPPoint;
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right, ptCenter.Y - 8 * h), });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right, rect.Bottom - radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right - radiu, rect.Bottom), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left + radiu, rect.Bottom), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left, rect.Bottom - radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left, rect.Top + radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left + radiu, rect.Top), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right - radiu, rect.Top), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right, rect.Top + radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right, rect.Top + 13 * h), });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = ptCPPoint, });
                        this.pathFigure.IsClosed = true;
                        break;
                    }
                case "a4":
                case "ad":   // 尖角在矩形右侧偏下
                    {
                        this.pathFigure.Segments.Clear();
                        this.pathFigure.StartPoint = ptCPPoint;
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right, rect.Bottom - 13 * h), });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right, rect.Bottom - radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right - radiu, rect.Bottom), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left + radiu, rect.Bottom), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left, rect.Bottom - radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left, rect.Top + radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left + radiu, rect.Top), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right - radiu, rect.Top), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right, rect.Top + radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right, ptCenter.Y + 8 * h), });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = ptCPPoint, });
                        this.pathFigure.IsClosed = true;
                        break;
                    }
                case "ae":
                case "a5":   // 尖角在矩形底部右侧
                    {
                        this.pathFigure.Segments.Clear();
                        this.pathFigure.StartPoint = ptCPPoint;
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(ptCenter.X + 8 * w, rect.Bottom), });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left + radiu, rect.Bottom), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left, rect.Bottom - radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left, rect.Top + radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left + radiu, rect.Top), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right - radiu, rect.Top), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right, rect.Top + radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right, rect.Bottom - radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right - radiu, rect.Bottom), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right - 13 * w, rect.Bottom), });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = ptCPPoint, });
                        this.pathFigure.IsClosed = true;
                        break;
                    }
                case "a6":
                case "af":   // 尖角在矩形底部左侧
                    {
                        this.pathFigure.Segments.Clear();
                        this.pathFigure.StartPoint = ptCPPoint;
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left + 13 * w, rect.Bottom), });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left + radiu, rect.Bottom), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left, rect.Bottom - radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left, rect.Top + radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left + radiu, rect.Top), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right - radiu, rect.Top), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right, rect.Top + radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right, rect.Bottom - radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right - radiu, rect.Bottom), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(ptCenter.X - 8 * w, rect.Bottom), });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = ptCPPoint, });
                        this.pathFigure.IsClosed = true;
                        break;
                    }
                case "ag":
                case "a7":   // 尖角在矩形左侧偏下
                    {
                        this.pathFigure.Segments.Clear();
                        this.pathFigure.StartPoint = ptCPPoint;
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left, ptCenter.Y + 8 * h), });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left, rect.Top + radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left + radiu, rect.Top), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right - radiu, rect.Top), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right, rect.Top + radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right, rect.Bottom - radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right - radiu, rect.Bottom), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left + radiu, rect.Bottom), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left, rect.Bottom - radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left, rect.Bottom - 13 * h), });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = ptCPPoint, });
                        this.pathFigure.IsClosed = true;
                        break;
                    }
                case "a8":
                case "ah":    // 尖角在矩形左侧偏上
                    {
                        this.pathFigure.Segments.Clear();
                        this.pathFigure.StartPoint = ptCPPoint;
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left, rect.Top + 13 * h), });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left, rect.Top + radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left + radiu, rect.Top), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right - radiu, rect.Top), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right, rect.Top + radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right, rect.Bottom - radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right - radiu, rect.Bottom), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left + radiu, rect.Bottom), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left, rect.Bottom - radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left, ptCenter.Y + 8 * h), });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = ptCPPoint, });
                        this.pathFigure.IsClosed = true;
                        break;
                    }
                default:    // 尖角点在矩形内部
                    {
                        // 只绘制一个圆角矩形
                        this.pathFigure.Segments.Clear();
                        this.pathFigure.StartPoint = new Point(rect.Left + radiu, rect.Top);
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right - radiu, rect.Top), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right, rect.Top + radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Right, rect.Bottom - radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Right - radiu, rect.Bottom), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left + radiu, rect.Bottom), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left, rect.Bottom - radiu), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.Segments.Add(new LineSegment() { Point = new Point(rect.Left, rect.Top + radiu), });
                        this.pathFigure.Segments.Add(new ArcSegment() { Point = new Point(rect.Left + radiu, rect.Top), Size = new Size(radiu, radiu), SweepDirection = SweepDirection.Clockwise, });
                        this.pathFigure.IsClosed = true;
                        break;
                    }
            }
        }

        /// <summary>
        /// 画思考气泡。
        /// </summary>
        private void DrawThinkingCloud(Point startPoint, Point centerCPPoint, Point endPoint)
        {
            var rect = new Rect(startPoint, endPoint);

            var points = new List<Point>();
            points.Add(new Point(40, 78));    // 起点
            points.Add(new Point(40, 58));    // 1
            points.Add(new Point(70, 38));
            points.Add(new Point(101, 50));
            points.Add(new Point(121, 28));   // 2
            points.Add(new Point(141, 38));
            points.Add(new Point(152, 46));
            points.Add(new Point(161, 28));   // 3
            points.Add(new Point(187, 33));
            points.Add(new Point(191, 44));
            points.Add(new Point(211, 28));   // 4
            points.Add(new Point(231, 28));
            points.Add(new Point(246, 53));
            points.Add(new Point(260, 58));   // 5
            points.Add(new Point(270, 64));
            points.Add(new Point(261, 84));
            points.Add(new Point(280, 100));  // 6
            points.Add(new Point(270, 120));
            points.Add(new Point(240, 128));
            points.Add(new Point(245, 155));  // 7
            points.Add(new Point(200, 155));
            points.Add(new Point(188, 148));
            points.Add(new Point(160, 180));  // 8
            points.Add(new Point(120, 170));
            points.Add(new Point(105, 155));
            points.Add(new Point(80, 170));   // 9
            points.Add(new Point(50, 160));
            points.Add(new Point(51, 143));
            points.Add(new Point(15, 134));   // 10
            points.Add(new Point(30, 100));
            points.Add(new Point(28, 102));
            points.Add(new Point(21, 97));    // 11
            points.Add(new Point(19, 76));
            points.Add(new Point(40, 78));
            points.Add(new Point(40, 78));    // 1
            points.Add(new Point(40, 79));
            points.Add(new Point(40.5, 81));
            points.Add(new Point(42, 83));
            points.Add(new Point(101, 50));   // 2
            points.Add(new Point(103, 51));
            points.Add(new Point(105.5, 52));
            points.Add(new Point(109, 55));
            points.Add(new Point(152, 46));   // 3
            points.Add(new Point(151, 48));
            points.Add(new Point(151, 48));
            points.Add(new Point(150, 50));
            points.Add(new Point(191, 44));   // 4
            points.Add(new Point(189, 46));
            points.Add(new Point(189, 46));
            points.Add(new Point(187, 48));
            points.Add(new Point(246, 53));   // 5
            points.Add(new Point(246.5, 54));
            points.Add(new Point(247.5, 57));
            points.Add(new Point(248, 59));
            points.Add(new Point(261, 84));   // 6
            points.Add(new Point(257, 90));
            points.Add(new Point(250, 92));
            points.Add(new Point(248, 92));
            points.Add(new Point(240, 128));  // 7
            points.Add(new Point(236, 116));
            points.Add(new Point(230, 112));
            points.Add(new Point(221, 106));
            points.Add(new Point(188, 148));  // 8
            points.Add(new Point(190, 145.5));
            points.Add(new Point(191, 143.5));
            points.Add(new Point(191, 142));
            points.Add(new Point(105, 155));  // 9
            points.Add(new Point(103, 152.5));
            points.Add(new Point(102.5, 151.5));
            points.Add(new Point(101, 149));
            points.Add(new Point(51, 143));   // 10
            points.Add(new Point(53.5, 143.5));
            points.Add(new Point(56, 143));
            points.Add(new Point(58, 142));
            points.Add(new Point(28, 102));   // 11
            points.Add(new Point(35, 107));
            points.Add(new Point(43, 105));
            points.Add(new Point(44, 105));

            #region 坐标照射到 rect 中对应位置，再加上左上角基准位置。
            var minX = double.MaxValue;
            var minY = double.MaxValue;
            var maxX = 0.0;
            var maxY = 0.0;

            foreach (var pt in points)
            {
                minX = Math.Min(pt.X, minX);
                minY = Math.Min(pt.Y, minY);
                maxX = Math.Max(pt.X, maxX);
                maxY = Math.Max(pt.Y, maxY);
            }

            var tempRect = new Rect(0, 0, maxX, maxY);

            var rx = rect.Width / tempRect.Width;
            var ry = rect.Height / tempRect.Height;

            var ptTemp = new List<Point>();
            for (int i = 0; i < points.Count; i++)
            {
                var pt = points[i];
                ptTemp.Add(new Point(pt.X - minX, pt.Y - minY));  // 归零
            }

            var pts = new List<Point>();
            for (int i = 0; i < ptTemp.Count; i++)
            {
                var pt = points[i];
                pts.Add(new Point(pt.X * rx + rect.Left, (pt.Y - 10) * ry + rect.Top));
            }

            #endregion

            this.pathFigure.Segments.Clear();

            this.pathFigure.StartPoint = pts[0];
            this.pathFigure.Segments.Add(new BezierSegment() { Point1 = pts[1], Point2 = pts[2], Point3 = pts[3], });
            this.pathFigure.Segments.Add(new BezierSegment() { Point1 = pts[4], Point2 = pts[5], Point3 = pts[6], });
            this.pathFigure.Segments.Add(new BezierSegment() { Point1 = pts[7], Point2 = pts[8], Point3 = pts[9], });
            this.pathFigure.Segments.Add(new BezierSegment() { Point1 = pts[10], Point2 = pts[11], Point3 = pts[12], });
            this.pathFigure.Segments.Add(new BezierSegment() { Point1 = pts[13], Point2 = pts[14], Point3 = pts[15], });
            this.pathFigure.Segments.Add(new BezierSegment() { Point1 = pts[16], Point2 = pts[17], Point3 = pts[18], });
            this.pathFigure.Segments.Add(new BezierSegment() { Point1 = pts[19], Point2 = pts[20], Point3 = pts[21], });
            this.pathFigure.Segments.Add(new BezierSegment() { Point1 = pts[22], Point2 = pts[23], Point3 = pts[24], });
            this.pathFigure.Segments.Add(new BezierSegment() { Point1 = pts[25], Point2 = pts[26], Point3 = pts[27], });
            this.pathFigure.Segments.Add(new BezierSegment() { Point1 = pts[28], Point2 = pts[29], Point3 = pts[30], });
            this.pathFigure.Segments.Add(new BezierSegment() { Point1 = pts[31], Point2 = pts[32], Point3 = pts[33], });

            var pf1 = new PathFigure() { StartPoint = pts[34], IsFilled = false, };
            pf1.Segments.Add(new BezierSegment() { Point1 = pts[35], Point2 = pts[36], Point3 = pts[37], });
            this.pathGeometry.Figures.Add(pf1);

            var pf2 = new PathFigure() { StartPoint = pts[38], IsFilled = false, };
            pf2.Segments.Add(new BezierSegment() { Point1 = pts[39], Point2 = pts[40], Point3 = pts[41], });
            this.pathGeometry.Figures.Add(pf2);

            var pf3 = new PathFigure() { StartPoint = pts[42], IsFilled = false, };
            pf3.Segments.Add(new BezierSegment() { Point1 = pts[43], Point2 = pts[44], Point3 = pts[45], });
            this.pathGeometry.Figures.Add(pf3);

            var pf4 = new PathFigure() { StartPoint = pts[46], IsFilled = false, };
            pf4.Segments.Add(new BezierSegment() { Point1 = pts[47], Point2 = pts[48], Point3 = pts[49], });
            this.pathGeometry.Figures.Add(pf4);

            var pf5 = new PathFigure() { StartPoint = pts[50], IsFilled = false, };
            pf5.Segments.Add(new BezierSegment() { Point1 = pts[51], Point2 = pts[52], Point3 = pts[53], });
            this.pathGeometry.Figures.Add(pf5);

            var pf6 = new PathFigure() { StartPoint = pts[54], IsFilled = false, };
            pf6.Segments.Add(new BezierSegment() { Point1 = pts[55], Point2 = pts[56], Point3 = pts[57], });
            this.pathGeometry.Figures.Add(pf6);

            var pf7 = new PathFigure() { StartPoint = pts[58], IsFilled = false, };
            pf7.Segments.Add(new BezierSegment() { Point1 = pts[59], Point2 = pts[60], Point3 = pts[61], });
            this.pathGeometry.Figures.Add(pf7);

            var pf8 = new PathFigure() { StartPoint = pts[62], IsFilled = false, };
            pf8.Segments.Add(new BezierSegment() { Point1 = pts[63], Point2 = pts[64], Point3 = pts[65], });
            this.pathGeometry.Figures.Add(pf8);

            var pf9 = new PathFigure() { StartPoint = pts[66], IsFilled = false, };
            pf9.Segments.Add(new BezierSegment() { Point1 = pts[67], Point2 = pts[68], Point3 = pts[69], });
            this.pathGeometry.Figures.Add(pf9);

            var pf10 = new PathFigure() { StartPoint = pts[70], IsFilled = false, };
            pf10.Segments.Add(new BezierSegment() { Point1 = pts[71], Point2 = pts[72], Point3 = pts[73], });
            this.pathGeometry.Figures.Add(pf10);

            var pf11 = new PathFigure() { StartPoint = pts[74], IsFilled = false, };
            pf11.Segments.Add(new BezierSegment() { Point1 = pts[75], Point2 = pts[76], Point3 = pts[77], });
            this.pathGeometry.Figures.Add(pf11);

            if (this.thinkingEllipse1 == null)
            {
                this.thinkingEllipse1 = new Ellipse();
                this.mainCanvas.Children.Add(this.thinkingEllipse1);
            }

            if (this.thinkingEllipse2 == null)
            {
                this.thinkingEllipse2 = new Ellipse();
                this.mainCanvas.Children.Add(this.thinkingEllipse2);
            }

            if (this.thinkingEllipse3 == null)
            {
                this.thinkingEllipse3 = new Ellipse();
                this.mainCanvas.Children.Add(this.thinkingEllipse3);
            }

            if (this.thinkingEllipse4 == null)
            {
                this.thinkingEllipse4 = new Ellipse();
                this.mainCanvas.Children.Add(this.thinkingEllipse4);
            }

            RefreshLineDash();

            this.thinkingEllipse1.Stroke = this.WidgetLineColor;
            this.thinkingEllipse1.StrokeThickness = this.WidgetLineWidth;
            this.thinkingEllipse1.Fill = this.WidgetBackColor;

            this.thinkingEllipse2.Stroke = this.WidgetLineColor;
            this.thinkingEllipse2.StrokeThickness = this.WidgetLineWidth;
            this.thinkingEllipse2.Fill = this.WidgetBackColor;

            this.thinkingEllipse3.Stroke = this.WidgetLineColor;
            this.thinkingEllipse3.StrokeThickness = this.WidgetLineWidth;
            this.thinkingEllipse3.Fill = this.WidgetBackColor;

            this.thinkingEllipse4.Stroke = this.WidgetLineColor;
            this.thinkingEllipse4.StrokeThickness = this.WidgetLineWidth;
            this.thinkingEllipse4.Fill = this.WidgetBackColor;

            this.thinkingEllipse1.Height =
                this.thinkingEllipse1.Width = Math.Min(rect.Width, rect.Height) / 20;
            this.thinkingEllipse2.Height =
                this.thinkingEllipse2.Width = Math.Min(rect.Width, rect.Height) / 16;
            this.thinkingEllipse3.Height =
                this.thinkingEllipse3.Width = Math.Min(rect.Width, rect.Height) / 12;
            this.thinkingEllipse4.Height =
                this.thinkingEllipse4.Width = Math.Min(rect.Width, rect.Height) / 8;

            Canvas.SetLeft(this.thinkingEllipse1, centerCPPoint.X - this.thinkingEllipse1.Width / 2);
            Canvas.SetTop(this.thinkingEllipse1, centerCPPoint.Y - this.thinkingEllipse1.Height / 2);

            var rectCenter = new Point(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2);
            var ept2 = GetInnerPoint(rectCenter, centerCPPoint, 0.1);
            Canvas.SetLeft(this.thinkingEllipse2, ept2.X - this.thinkingEllipse2.Width / 2);
            Canvas.SetTop(this.thinkingEllipse2, ept2.Y - this.thinkingEllipse2.Height / 2);

            var ept3 = GetInnerPoint(rectCenter, centerCPPoint, 0.25);
            Canvas.SetLeft(this.thinkingEllipse3, ept3.X - this.thinkingEllipse3.Width / 2);
            Canvas.SetTop(this.thinkingEllipse3, ept3.Y - this.thinkingEllipse3.Height / 2);

            var ept4 = GetInnerPoint(rectCenter, centerCPPoint, 0.4);
            Canvas.SetLeft(this.thinkingEllipse4, ept4.X - this.thinkingEllipse4.Width / 2);
            Canvas.SetTop(this.thinkingEllipse4, ept4.Y - this.thinkingEllipse4.Height / 2);
        }

        /// <summary>
        /// 仅供思考框使用。
        /// </summary>
        private Ellipse thinkingEllipse1 = null;
        /// <summary>
        /// 仅供思考框使用。
        /// </summary>
        private Ellipse thinkingEllipse2 = null;
        /// <summary>
        /// 仅供思考框使用。
        /// </summary>
        private Ellipse thinkingEllipse3 = null;
        /// <summary>
        /// 仅供思考框使用。
        /// </summary>
        private Ellipse thinkingEllipse4 = null;

        private bool GetOrgPoints(Point startPoint, Point centerCPPoint, Point endPoint, out Point? point1, out Point? point2, out Point? point3)
        {
            var x1 = startPoint.X;
            var y1 = startPoint.Y;
            var x2 = endPoint.X;
            var y2 = endPoint.Y;
            if (x2 != x1)
            {
                var k = (y2 - y1) / (x2 - x1);
                var x = (k * k * x1 + k * (centerCPPoint.Y - y1) + centerCPPoint.X) / (k * k + 1);
                var y = k * (x - x1) + y1;

                var h = (centerCPPoint.Y - y) / 2;
                var w = (centerCPPoint.X - x) / 2;

                point1 = new Point(startPoint.X + w, startPoint.Y + h);
                point2 = new Point(x + w, y + h);
                point3 = new Point(endPoint.X + w, endPoint.Y + h);
                return true;
            }
            else
            {
                point1 = new Point(startPoint.X + (centerCPPoint.X - startPoint.X) / 2, startPoint.Y);
                point2 = new Point(startPoint.X + (centerCPPoint.X - startPoint.X) / 2, centerCPPoint.Y);
                point3 = new Point(endPoint.X + (centerCPPoint.X - endPoint.X) / 2, endPoint.Y);
                return true;
            }
        }

        /// <summary>
        /// 返回垂足与中控点的中点。如果找不到，就返回中点。
        /// </summary>
        private bool GetYPoint(Point startPoint, Point centerCPPoint, Point endPoint, out Point? yPoint)
        {
            var x1 = startPoint.X;
            var y1 = startPoint.Y;
            var x2 = endPoint.X;
            var y2 = endPoint.Y;
            if (x2 != x1)
            {
                var k = (y2 - y1) / (x2 - x1);
                var x = (k * k * x1 + k * (centerCPPoint.Y - y1) + centerCPPoint.X) / (k * k + 1);
                var y = k * (x - x1) + y1;
                yPoint = new Point(x + (centerCPPoint.X - x) / 2, y + (centerCPPoint.Y - y) / 2);
                return true;
            }
            else
            {
                yPoint = new Point(startPoint.X + (endPoint.X - startPoint.X) / 2, startPoint.Y + (endPoint.Y - startPoint.Y) / 2);
                return true;
            }
        }

        /// <summary>
        /// 返回垂足。如果找不到，就返回中点。
        /// </summary>
        private bool GetTPoint(Point startPoint, Point centerCPPoint, Point endPoint, out Point? tPoint)
        {
            var x1 = startPoint.X;
            var y1 = startPoint.Y;
            var x2 = endPoint.X;
            var y2 = endPoint.Y;
            if (x2 != x1)
            {
                var k = (y2 - y1) / (x2 - x1);
                var x = (k * k * x1 + k * (centerCPPoint.Y - y1) + centerCPPoint.X) / (k * k + 1);
                var y = k * (x - x1) + y1;
                tPoint = new Point(x, y);
                return true;
            }
            else
            {
                tPoint = new Point(startPoint.X + (endPoint.X - startPoint.X) / 2, startPoint.Y + (endPoint.Y - startPoint.Y) / 2);
                return true;
            }
        }

        private bool GetBezierControlPoints(Point startPoint, Point centerCPPoint, Point endPoint, out Point? point1, out Point? point2, out Point? point3)
        {
            // 这三个点是指由 起点、终点、中控点构成的矩形的两个中点及矩形中心点。
            // 这两个边线中点是指除 SE 和 经过 C 点的两边之外的另两边的中点。
            // 先计算矩形。
            // 基本思路是先算出 C 到 SE 的垂足坐标，然后推导出 S1 和 E1。

            // 先计算出 Ax+By+C=0 这个方程，即先求出 ABC 

            var x1 = startPoint.X;
            var y1 = startPoint.Y;
            var x2 = endPoint.X;
            var y2 = endPoint.Y;
            if (x2 != x1)
            {
                var k = (y2 - y1) / (x2 - x1);
                var x = (k * k * x1 + k * (centerCPPoint.Y - y1) + centerCPPoint.X) / (k * k + 1);
                var y = k * (x - x1) + y1;

                var h = (centerCPPoint.Y - y);
                var w = (centerCPPoint.X - x);

                point1 = new Point(startPoint.X + w, startPoint.Y + h);
                point2 = new Point(x, y);
                point3 = new Point(endPoint.X + w, endPoint.Y + h);
                return true;
            }
            else
            {
                point1 = new Point(startPoint.X + (centerCPPoint.X - startPoint.X) / 2, startPoint.Y);
                point2 = new Point(startPoint.X + (centerCPPoint.X - startPoint.X) / 2, centerCPPoint.Y);
                point3 = new Point(endPoint.X + (centerCPPoint.X - endPoint.X) / 2, endPoint.Y);
                return true;
            }
        }

        private bool LargeArc(Point startPoint, Point centerCPPoint, Point endPoint)
        {
            if (startPoint.X == centerCPPoint.X)
            {
                if (startPoint.Y >= centerCPPoint.Y)
                {
                    if (endPoint.X >= startPoint.X) return true;
                    else return false;
                }
                else
                {
                    if (endPoint.X >= startPoint.X) return false;
                    else return true;
                }
            }
            else if (startPoint.Y == centerCPPoint.Y)
            {
                if (startPoint.X >= centerCPPoint.X)
                {
                    if (endPoint.Y > startPoint.Y) return false;
                    else return true;
                }
                else
                {
                    if (endPoint.Y > startPoint.Y) return true;
                    else return false;
                }
            }

            //分象限
            //一、四象限，E点在SC线上侧需要大弧，而下侧需要小弧
            //二、三象限，E点在SC线上侧需要小弧，而下侧需要大弧
            if (startPoint.X < centerCPPoint.X)//二、三象限
            {
                double k = (startPoint.Y - centerCPPoint.Y) / (startPoint.X - centerCPPoint.X);

                double y = endPoint.X * k + centerCPPoint.Y - k * centerCPPoint.X;
                if (endPoint.Y > y) return true;
                else return false;
            }
            else//一、四象限
            {
                double k = (startPoint.Y - centerCPPoint.Y) / (startPoint.X - centerCPPoint.X);

                double y = endPoint.X * k + centerCPPoint.Y - k * centerCPPoint.X;
                if (endPoint.Y > y) return false;
                else return true;
            }
        }

        /// <summary>
        /// 重定文本位置。
        /// </summary>
        /// <param name="startPoint">首端点。</param>
        /// <param name="centerCPPoint">中控制点。</param>
        /// <param name="endPoint">尾端点。</param>
        private void LocateTextPanel(Point startPoint, Point centerCPPoint, Point endPoint)
        {
            switch (this.triangleForm)
            {
                //case Enums.TriangleForm.Sector:
                //case Enums.TriangleForm.Pie:
                //这俩必须取消。因为扇形和饼形需要其它点的坐标，因此在DrawLine()方法中实现了。
                case Enums.TriangleForm.Triangle:
                    {
                        Point textStart;

                        double left, right, bottom, top;
                        left = Math.Min(Math.Min(startPoint.X, centerCPPoint.X), endPoint.X);
                        right = Math.Max(Math.Max(startPoint.X, centerCPPoint.X), endPoint.X);
                        top = Math.Min(Math.Min(startPoint.Y, centerCPPoint.Y), endPoint.Y);
                        bottom = Math.Max(Math.Max(startPoint.Y, centerCPPoint.Y), endPoint.Y);

                        textStart = new Point(left + (right - left) / 2 - (this.mainTextPanel.ActualWidth / 2) - this.mainTextPanel.Margin.Left,
                            top + (bottom - top) / 2 - (this.mainTextPanel.ActualHeight / 2) - this.mainTextPanel.Margin.Top);

                        Canvas.SetLeft(this.mainTextPanel, textStart.X);
                        Canvas.SetTop(this.mainTextPanel, textStart.Y);
                        break;
                    }
                case TriangleForm.Bracket:
                case TriangleForm.TLine:
                case TriangleForm.OrgLine:
                case TriangleForm.YLine:
                    {
                        var rect = new Rect(startPoint, endPoint);
                        if (GetTPoint(startPoint, centerCPPoint, endPoint, out Point? tPoint))
                        {
                            var length = this.mainTextPanel.ActualHeight / 2 + 5 * this.WidgetLineWidth;
                            var extendedPoint = GetExtendedPoint(tPoint.Value, centerCPPoint, length);
                            Canvas.SetLeft(this.mainTextPanel, extendedPoint.X - this.mainTextPanel.ActualWidth / 2);
                            Canvas.SetTop(this.mainTextPanel, extendedPoint.Y - this.mainTextPanel.ActualHeight / 2);
                        }
                        else
                        {
                            Canvas.SetLeft(this.mainTextPanel, centerCPPoint.X - this.mainTextPanel.ActualWidth / 2);
                            Canvas.SetTop(this.mainTextPanel, centerCPPoint.Y - this.mainTextPanel.ActualHeight / 2);
                        }

                        if (startPoint.X != endPoint.X)
                        {
                            if (startPoint.Y != endPoint.Y)
                            {
                                var tan = (startPoint.Y - endPoint.Y) / (startPoint.X - endPoint.X);
                                double angleOfLine = Math.Atan2((endPoint.Y - startPoint.Y), (endPoint.X - startPoint.X)) * 180 / Math.PI;
                                if (angleOfLine > 180)
                                    angleOfLine -= 180;   // 照顾人的视角

                                this.textRotateAngleAuto = angleOfLine;
                            }
                            else
                            {
                                this.textRotateAngleAuto = 0;  // 人的观察视角总是方便从上向下的，不需要 180度。
                            }
                        }
                        else
                        {
                            if (endPoint.Y > startPoint.Y)
                            {
                                this.textRotateAngleAuto = 90;
                            }
                            else if (endPoint.Y == startPoint.Y)
                            {
                                this.textRotateAngleAuto = 0;
                            }
                            else
                            {
                                this.textRotateAngleAuto = -90;
                            }
                        }

                        //RefreshTextRotateAngle();  // 会自动调用，不必在这里写
                        break;
                    }
                case TriangleForm.DialogBubble:
                case TriangleForm.ThinkingBubble:
                    {
                        var rect = new Rect(startPoint, endPoint);
                        var ptCenter = new Point(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2);
                        var textStart = new Point(ptCenter.X - (this.mainTextPanel.ActualWidth / 2) - this.mainTextPanel.Margin.Left,
                            ptCenter.Y - (this.mainTextPanel.ActualHeight / 2) - this.mainTextPanel.Margin.Top);

                        Canvas.SetLeft(this.mainTextPanel, textStart.X);
                        Canvas.SetTop(this.mainTextPanel, textStart.Y);
                        break;
                    }
            }
        }

        /// <summary>
        /// 已知直线上两点（ptFrom, ptTo），直线上距离 ptTo 长度为 length 的点的坐标（该点与 ptFrom 分别在 ptTo 的两侧）。
        /// </summary>
        /// <param name="ptFrom"></param>
        /// <param name="ptTo"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        private Point GetExtendedPoint(Point ptFrom, Point ptTo, double length)
        {
            if (ptFrom.X == ptTo.X && ptFrom.Y == ptTo.Y) return ptTo;  // 直接返回。

            if (ptFrom.X == ptTo.X)
            {
                if (ptFrom.Y > ptTo.Y) return new Point(ptFrom.X, ptTo.Y - length);

                return new Point(ptFrom.X, ptTo.Y + length);
            }

            if (ptFrom.Y == ptTo.Y)
            {
                if (ptFrom.X > ptTo.X) return new Point(ptTo.X - length, ptFrom.Y);

                return new Point(ptTo.X + length, ptFrom.Y);
            }

            // T 在 F 左上
            if (ptTo.X < ptFrom.X && ptTo.Y < ptFrom.Y)
            {
                var w = Math.Abs(ptFrom.X - ptTo.X);
                var h = Math.Abs(ptFrom.Y - ptTo.Y);
                var r = Math.Sqrt(Math.Pow(Math.Abs(w), 2) + Math.Pow(Math.Abs(h), 2));

                var rate_w = w / r;
                var rate_h = h / r;

                var newX = ptFrom.X - Math.Sqrt(Math.Pow(r + length, 2)) * rate_w;
                var newY = ptFrom.Y - Math.Sqrt(Math.Pow(r + length, 2)) * rate_h;
                return new Point(newX, newY);
            }

            if (ptTo.X > ptFrom.X && ptTo.Y < ptFrom.Y)
            {
                var w = Math.Abs(ptFrom.X - ptTo.X);
                var h = Math.Abs(ptFrom.Y - ptTo.Y);
                var r = Math.Sqrt(Math.Pow(Math.Abs(w), 2) + Math.Pow(Math.Abs(h), 2));

                var rate_w = w / r;
                var rate_h = h / r;

                var newX = ptFrom.X + Math.Sqrt(Math.Pow(r + length, 2)) * rate_w;
                var newY = ptFrom.Y - Math.Sqrt(Math.Pow(r + length, 2)) * rate_h;
                return new Point(newX, newY);
            }

            if (ptTo.X > ptFrom.X && ptTo.Y > ptFrom.Y)
            {
                var w = Math.Abs(ptFrom.X - ptTo.X);
                var h = Math.Abs(ptFrom.Y - ptTo.Y);
                var r = Math.Sqrt(Math.Pow(Math.Abs(w), 2) + Math.Pow(Math.Abs(h), 2));

                var rate_w = w / r;
                var rate_h = h / r;

                var newX = ptFrom.X + Math.Sqrt(Math.Pow(r + length, 2)) * rate_w;
                var newY = ptFrom.Y + Math.Sqrt(Math.Pow(r + length, 2)) * rate_h;
                return new Point(newX, newY);
            }

            if (ptTo.X < ptFrom.X && ptTo.Y > ptFrom.Y)
            {
                var w = Math.Abs(ptFrom.X - ptTo.X);
                var h = Math.Abs(ptFrom.Y - ptTo.Y);
                var r = Math.Sqrt(Math.Pow(Math.Abs(w), 2) + Math.Pow(Math.Abs(h), 2));

                var rate_w = w / r;
                var rate_h = h / r;

                var newX = ptFrom.X - Math.Sqrt(Math.Pow(r + length, 2)) * rate_w;
                var newY = ptFrom.Y + Math.Sqrt(Math.Pow(r + length, 2)) * rate_h;
                return new Point(newX, newY);
            }

            return ptTo;
        }

        /// <summary>
        /// 已知直线上两点（ptFrom, ptTo），直线上距离 ptTo 长度为 length 的点的坐标（该点在 ptFrom 和 ptTo 之间，length 则由 rage 参数决定，rate指两点间距的 几分之一）。
        /// ★这个方法与 GetExtendedPoint() 正好相反。（GetExtendedPoint() 获取的点在 ptTo 外侧。
        /// </summary>
        /// <param name="ptFrom"></param>
        /// <param name="ptTo"></param>
        /// <param name="rate">距 ptTo 的距离是 ptFrom 和 ptTo 间距的几分之一。</param>
        /// <returns></returns>
        private Point GetInnerPoint(Point ptFrom, Point ptTo, double rate)
        {
            if (ptFrom.X == ptTo.X && ptFrom.Y == ptTo.Y) return ptTo;  // 直接返回。

            double length;

            if (ptFrom.X == ptTo.X)
            {
                length = Math.Abs(ptFrom.Y - ptTo.Y) * rate;
                if (ptFrom.Y > ptTo.Y)
                {
                    return new Point(ptFrom.X, ptTo.Y + length);
                }

                return new Point(ptFrom.X, ptTo.Y - length);
            }

            if (ptFrom.Y == ptTo.Y)
            {
                length = Math.Abs(ptFrom.X - ptTo.X) * rate;

                if (ptFrom.X > ptTo.X)
                {
                    return new Point(ptTo.X + length, ptFrom.Y);
                }

                return new Point(ptTo.X - length, ptFrom.Y);
            }

            // T 在 F 左上
            if (ptTo.X < ptFrom.X && ptTo.Y < ptFrom.Y)
            {
                var w = Math.Abs(ptFrom.X - ptTo.X);
                var h = Math.Abs(ptFrom.Y - ptTo.Y);
                var r = Math.Sqrt(Math.Pow(Math.Abs(w), 2) + Math.Pow(Math.Abs(h), 2));

                var rate_w = w / r;
                var rate_h = h / r;

                length = r * rate;

                var newX = ptFrom.X - Math.Sqrt(Math.Pow(r - length, 2)) * rate_w;
                var newY = ptFrom.Y - Math.Sqrt(Math.Pow(r - length, 2)) * rate_h;
                return new Point(newX, newY);
            }

            if (ptTo.X > ptFrom.X && ptTo.Y < ptFrom.Y)
            {
                var w = Math.Abs(ptFrom.X - ptTo.X);
                var h = Math.Abs(ptFrom.Y - ptTo.Y);
                var r = Math.Sqrt(Math.Pow(Math.Abs(w), 2) + Math.Pow(Math.Abs(h), 2));

                var rate_w = w / r;
                var rate_h = h / r;

                length = r * rate;

                var newX = ptFrom.X + Math.Sqrt(Math.Pow(r - length, 2)) * rate_w;
                var newY = ptFrom.Y - Math.Sqrt(Math.Pow(r - length, 2)) * rate_h;
                return new Point(newX, newY);
            }

            if (ptTo.X > ptFrom.X && ptTo.Y > ptFrom.Y)
            {
                var w = Math.Abs(ptFrom.X - ptTo.X);
                var h = Math.Abs(ptFrom.Y - ptTo.Y);
                var r = Math.Sqrt(Math.Pow(Math.Abs(w), 2) + Math.Pow(Math.Abs(h), 2));

                var rate_w = w / r;
                var rate_h = h / r;

                length = r * rate;

                var newX = ptFrom.X + Math.Sqrt(Math.Pow(r - length, 2)) * rate_w;
                var newY = ptFrom.Y + Math.Sqrt(Math.Pow(r - length, 2)) * rate_h;
                return new Point(newX, newY);
            }

            if (ptTo.X < ptFrom.X && ptTo.Y > ptFrom.Y)
            {
                var w = Math.Abs(ptFrom.X - ptTo.X);
                var h = Math.Abs(ptFrom.Y - ptTo.Y);
                var r = Math.Sqrt(Math.Pow(Math.Abs(w), 2) + Math.Pow(Math.Abs(h), 2));

                var rate_w = w / r;
                var rate_h = h / r;

                length = r * rate;

                var newX = ptFrom.X - Math.Sqrt(Math.Pow(r - length, 2)) * rate_w;
                var newY = ptFrom.Y + Math.Sqrt(Math.Pow(r - length, 2)) * rate_h;
                return new Point(newX, newY);
            }

            return ptTo;
        }

        private Point FormatStartMovingPoint(Point newInsideMovingPoint, Point endPoint)
        {
            double horizontalDistance = newInsideMovingPoint.X - endPoint.X;
            double verticalDistance = newInsideMovingPoint.Y - endPoint.Y;

            double absHD = Math.Abs(horizontalDistance);
            double absVD = Math.Abs(verticalDistance);

            if (absVD > absHD)
            {
                if (absVD > absHD * 2)
                {
                    newInsideMovingPoint = new Point(endPoint.X, newInsideMovingPoint.Y);
                }
                else
                {
                    int qua = Getquadrant(newInsideMovingPoint, endPoint);
                    switch (qua)
                    {
                        case 1:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y - (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 2:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y + (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 3:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y - (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 4:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                    endPoint.Y + (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                    }
                }
            }
            else
            {
                if (absVD * 2 < absHD)
                {
                    newInsideMovingPoint = new Point(newInsideMovingPoint.X, endPoint.Y);
                }
                else
                {
                    int qua = Getquadrant(newInsideMovingPoint, endPoint);
                    switch (qua)
                    {
                        case 1:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y - (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 2:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y + (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 3:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y - (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 4:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                    endPoint.Y + (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                    }
                }
            }

            return newInsideMovingPoint;
        }

        /// <summary>
        /// 取与自身挂接的连接线。
        /// </summary>
        /// <returns>可能返回null。</returns>
        public List<ILinkableLine> GetLinkedLines()
        {
            if (this.masterEditor == null) return null;

            List<Widget> widgets = new List<Widget>();
            widgets.Add(this);

            return this.masterEditor.GetLinkedLines(widgets);
        }

        /// <summary>
        /// 以basePoint为基础坐标，画出四象限。
        /// 取出pt在哪个象限中。
        /// </summary>
        /// <param name="pt"></param>
        /// <param name="basePoint"></param>
        /// <returns></returns>
        private int Getquadrant(Point pt, Point basePoint)
        {
            if (pt.X > basePoint.X)
            {
                if (pt.Y > basePoint.Y)
                {
                    return 4;
                }
                else
                {
                    return 1;
                }
            }
            else
            {
                if (pt.Y > basePoint.Y)
                {
                    return 3;
                }
                else
                {
                    return 2;
                }
            }
        }

        public override string GetRelativeOuterXml(Point baseCopyTopLeft)
        {
            if (this.xmlData == null) return string.Empty;

            Point oldStartPoint = startPoint;
            Point newStartPoint = new Point(oldStartPoint.X - baseCopyTopLeft.X,
                oldStartPoint.Y - baseCopyTopLeft.Y);
            this.xmlData.SetAttribute(XmlTags.StartPointTag, newStartPoint.ToString());

            Point oldCenterCPPoint = centerCPPoint;
            Point newCenterCPPoint = new Point(oldCenterCPPoint.X - baseCopyTopLeft.X,
                oldCenterCPPoint.Y - baseCopyTopLeft.Y);
            this.xmlData.SetAttribute(XmlTags.CenterCPPointTag, newCenterCPPoint.ToString());

            Point oldEndPoint = endPoint;
            Point newEndPoint = new Point(oldEndPoint.X - baseCopyTopLeft.X,
                oldEndPoint.Y - baseCopyTopLeft.Y);
            this.xmlData.SetAttribute(XmlTags.EndPointTag, newEndPoint.ToString());

            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            sb.Append(this.xmlData.OuterXml);

            this.xmlData.SetAttribute(XmlTags.StartPointTag, oldStartPoint.ToString());
            this.xmlData.SetAttribute(XmlTags.CenterCPPointTag, oldCenterCPPoint.ToString());
            this.xmlData.SetAttribute(XmlTags.EndPointTag, oldEndPoint.ToString());
            return sb.ToString();
        }

        public event EventHandler<MouseButtonEventArgs> ControlHandlerDoubleClicked;

        protected void OnControlHandlerDoubleClicked(object sender, MouseButtonEventArgs e)
        {
            if (ControlHandlerDoubleClicked != null)
            {
                ControlHandlerDoubleClicked(sender, e);
            }
        }

        /// <summary>
        /// 这个虚方法是用以查看本部件是否在选定框的内部。
        /// 线型部件，各有各的计算办法。
        /// </summary>
        /// <param name="rect"></param>
        /// <returns></returns>
        public override bool IsInRect(Rect rect)
        {
            //return base.IsInRect(rect);//这个要屏蔽。
            bool isInRect = base.IsInRect(rect);
            if (isInRect)
            {
                return true;
            }
            else
            {
                Rect virtualRect = new Rect(TopLeft, BottomRight);
                return rect.IntersectsWith(virtualRect);
            }
        }

        public override void RefreshLineDash()
        {
            switch (lineDash)
            {
                case LineDashType.DashType.Dash:
                    {
                        //this.mainPolygon.StrokeDashArray = LineDashType.dashCollection; break;
                        this.mainPath.StrokeDashArray = LineDashType.dashCollection;
                        if (this.thinkingEllipse1 != null)
                            thinkingEllipse1.StrokeDashArray = LineDashType.dashCollection;
                        break;
                    }
                case LineDashType.DashType.DashDotDot:
                    {
                        //this.mainPolygon.StrokeDashArray = LineDashType.dashDotDotCollection; break;
                        this.mainPath.StrokeDashArray = LineDashType.dashDotDotCollection;
                        if (this.thinkingEllipse1 != null)
                            thinkingEllipse1.StrokeDashArray = LineDashType.dashDotDotCollection;
                        break;
                    }
                case LineDashType.DashType.Dot:
                    {
                        //this.mainPolygon.StrokeDashArray = LineDashType.dotCollection; break;
                        this.mainPath.StrokeDashArray = LineDashType.dotCollection;
                        if (this.thinkingEllipse1 != null)
                            thinkingEllipse1.StrokeDashArray = LineDashType.dotCollection;
                        break;
                    }
                case LineDashType.DashType.Solid:
                    {
                        //this.mainPolygon.StrokeDashArray = LineDashType.solidCollection; break;
                        this.mainPath.StrokeDashArray = LineDashType.solidCollection;
                        if (this.thinkingEllipse1 != null)
                            thinkingEllipse1.StrokeDashArray = LineDashType.solidCollection;
                        break;
                    }
                default:
                    {
                        //this.mainPolygon.StrokeDashArray = LineDashType.dashDotCollection; break;
                        this.mainPath.StrokeDashArray = LineDashType.dashDotCollection;
                        if (this.thinkingEllipse1 != null)
                            thinkingEllipse1.StrokeDashArray = LineDashType.dashDotCollection;
                        break;
                    }
            }
        }

        public override void RefreshWidgetLineWidth()
        {
            //mainPolygon.StrokeThickness = widgetLineWidth;
            mainPath.StrokeThickness = widgetLineWidth;

            if (thinkingEllipse4 != null) thinkingEllipse4.StrokeThickness = widgetLineWidth;
            if (thinkingEllipse3 != null) thinkingEllipse3.StrokeThickness = widgetLineWidth;
            if (thinkingEllipse2 != null) thinkingEllipse2.StrokeThickness = widgetLineWidth;
            if (thinkingEllipse1 != null) thinkingEllipse1.StrokeThickness = widgetLineWidth;
        }

        public override void RefreshLocation()
        {
            base.RefreshLocation();

            //mainPolygon.Points = new PointCollection(){
            //    startPoint,centerCPPoint,endPoint,startPoint,
            //};
            DrawLine();

            this.movingRect = new Rect(this.TopLeft, this.BottomRight);

            RefreshWidgetLineWidth();
            RefreshTextRotateAngle();

            this.RefreshCommentText();
            this.RefreshHyperLinkText();
        }

        public override void RefreshPointWhenGroupIn(Point baseTopLeft)
        {
            Point oldStartPoint = startPoint;
            Point newStartPoint = new Point(oldStartPoint.X - baseTopLeft.X,
                oldStartPoint.Y - baseTopLeft.Y);
            StartPoint = newStartPoint;

            Point oldCenterCPPoint = centerCPPoint;
            Point newCenterCPPoint = new Point(oldCenterCPPoint.X - baseTopLeft.X,
                oldCenterCPPoint.Y - baseTopLeft.Y);
            CenterCPPoint = newCenterCPPoint;

            Point oldEndPoint = endPoint;
            Point newEndPoint = new Point(oldEndPoint.X - baseTopLeft.X,
                oldEndPoint.Y - baseTopLeft.Y);
            EndPoint = newEndPoint;
        }

        public override void RefreshPointWhenGroupOut(Point baseTopLeft)
        {
            Point oldStartPoint = startPoint;
            Point newStartPoint = new Point(oldStartPoint.X + baseTopLeft.X,
                oldStartPoint.Y + baseTopLeft.Y);
            StartPoint = newStartPoint;

            Point oldCenterCPPoint = centerCPPoint;
            Point newCenterCPPoint = new Point(oldCenterCPPoint.X + baseTopLeft.X,
                oldCenterCPPoint.Y + baseTopLeft.Y);
            CenterCPPoint = newCenterCPPoint;

            Point oldEndPoint = endPoint;
            Point newEndPoint = new Point(oldEndPoint.X + baseTopLeft.X,
                oldEndPoint.Y + baseTopLeft.Y);
            EndPoint = newEndPoint;
        }

        public override void RefreshWidgetBackColor()
        {
            base.RefreshWidgetBackColor();
            if (widgetBackColor == Brushes.Transparent)
            {
                //mainPolygon.Fill = null;
                mainPath.Fill = null;

                if (thinkingEllipse4 != null) thinkingEllipse4.Fill = Brushes.Transparent;
                if (thinkingEllipse3 != null) thinkingEllipse3.Fill = Brushes.Transparent;
                if (thinkingEllipse2 != null) thinkingEllipse2.Fill = Brushes.Transparent;
                if (thinkingEllipse1 != null) thinkingEllipse1.Fill = Brushes.Transparent;
            }
            else
            {
                //mainPolygon.Fill = widgetBackColor;
                mainPath.Fill = widgetBackColor;

                if (thinkingEllipse4 != null) thinkingEllipse4.Fill = widgetBackColor;
                if (thinkingEllipse3 != null) thinkingEllipse3.Fill = widgetBackColor;
                if (thinkingEllipse2 != null) thinkingEllipse2.Fill = widgetBackColor;
                if (thinkingEllipse1 != null) thinkingEllipse1.Fill = widgetBackColor;
            }
        }

        public override void RefreshWidgetLineColor()
        {
            //this.mainPolygon.Stroke = widgetLineColor;
            this.mainPath.Stroke = widgetLineColor;

            if (thinkingEllipse4 != null) thinkingEllipse4.Stroke = widgetLineColor;
            if (thinkingEllipse3 != null) thinkingEllipse3.Stroke = widgetLineColor;
            if (thinkingEllipse2 != null) thinkingEllipse2.Stroke = widgetLineColor;
            if (thinkingEllipse1 != null) thinkingEllipse1.Stroke = widgetLineColor;
        }

        public override void RefreshIsShadowVisible()
        {
            base.RefreshIsShadowVisible();

            if (isShadowVisible)
            {
                //this.mainPolygon.Effect = Widget.ShadowEffect;
                this.mainPath.Effect = Widget.ShadowEffect;
            }
            else
            {
                //this.mainPolygon.Effect = null;
                this.mainPath.Effect = null;
            }
        }

        public override void RefreshTextPanelLocatin()
        {
            switch (this.triangleForm)
            {
                case Enums.TriangleForm.Triangle:
                    {
                        LocateTextPanel(this.startPoint, this.centerCPPoint, this.endPoint);
                        break;
                    }
                default:
                    {
                        DrawLine(this.startPoint, this.centerCPPoint, this.endPoint);
                        break;
                    }
            }
        }

        /// <summary>
        /// 刷新文本区旋转角度。
        /// </summary>
        public void RefreshTextRotateAngle()
        {
            switch (this.triangleForm)
            {
                case TriangleForm.Bracket:
                case TriangleForm.TLine:
                case TriangleForm.OrgLine:
                case TriangleForm.YLine:
                    {
                        if (this.mainTextPanel.RenderTransformOrigin != DefaultRenderCenter)
                        {
                            this.mainTextPanel.RenderTransformOrigin = DefaultRenderCenter;
                        }

                        if (this.textRotateAngleAuto == 0)
                        {
                            this.mainTextPanel.RenderTransform = DefaultRotateTransform;
                        }
                        else
                        {
                            if (this.textRotateAngleAuto > 90)
                            {
                                this.textRotateAngleAuto -= 180;
                            }
                            else if (this.textRotateAngleAuto < -90)
                            {
                                this.textRotateAngleAuto += 180;
                            }

                            this.mainTextPanel.RenderTransform = new RotateTransform(textRotateAngleAuto);
                        }
                        break;
                    }
                default:
                    {
                        if (this.mainTextPanel.RenderTransformOrigin != DefaultRenderCenter)
                        {
                            this.mainTextPanel.RenderTransformOrigin = DefaultRenderCenter;
                        }

                        if (this.textRotateAngle == 0)
                        {
                            this.mainTextPanel.RenderTransform = DefaultRotateTransform;
                        }
                        else
                        {
                            this.mainTextPanel.RenderTransform = new RotateTransform(textRotateAngle);
                        }
                        break;
                    }
            }
        }

        #endregion




        #region 其它=========================================================================================================

        //private enum ControlDraggingType { Start, End, None, CenterCP }
        //2012年5月30日已移植并合并至Enums.LineCtrlDraggingType文件中。

        private static DoubleCollection dashArray;

        #endregion

        /// <summary>
        /// 用以判断点在矩形的哪个区域。返回字符串，形如“a”+“number”（四正向）或“a”+“letter”（四隅向）（前一个“a”表示“area”）。例如：“a2”表示在矩形正上方偏右侧。
        ///   ①点在矩形正上方
        ///         中垂直线左侧：a1；中垂直线右侧：a2
        ///   ②点在矩形正右方
        ///         中水平线上侧：a3；中水平线下侧：a4
        ///   ③点在矩形正下方
        ///         中垂直线左侧：a6；中垂直线右侧：a5
        ///   ④点在矩形正左方
        ///         中水平线上侧：a8；中水平线下侧：a7
        ///   ⑤点在矩形左上角
        ///         在对角线上侧：aa；在对角线下侧：ah
        ///   ⑥点在矩形右上角
        ///         在对角线上侧：ab；在对角线下侧：ac
        ///   ⑦点在矩形右下角
        ///         在对角线上侧：ad；在对角线下侧：ae
        ///   ⑧点在矩形左下角
        ///         在对角线上侧：ag；在对角线下侧：af
        ///   ⑨点在矩形四边或内部：in
        ///   ⑩（理论上不存在）未能计算：unknown
        /// </summary>
        /// <param name="pt"></param>
        /// <param name="rect"></param>
        /// <returns></returns>
        public static string GetPointArea(Point pt, Rect rect)
        {
            // 点在矩形内部
            if (pt.X >= rect.Left && pt.X <= rect.Right && pt.Y >= rect.Top && pt.Y <= rect.Bottom) return "in";

            var ptcenter = new Point(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2);

            // 四正向（注意：不在矩形边上）
            // 点在矩形正上方
            if (pt.X >= rect.Left && pt.X <= rect.Right && pt.Y < rect.Top)
            {
                return (pt.X <= ptcenter.X) ? "a1" : "a2";
            }

            // 点在矩形正右方
            if (pt.X > rect.Right && pt.Y >= rect.Top && pt.Y <= rect.Bottom)
            {
                return (pt.Y <= ptcenter.Y) ? "a3" : "a4";
            }

            // 点在矩形正下方
            if (pt.X >= rect.Left && pt.X <= rect.Right && pt.Y > rect.Bottom)
            {
                return (pt.X <= ptcenter.X) ? "a6" : "a5";
            }

            // 点在矩形正左方
            if (pt.X < rect.Left && pt.Y >= rect.Top && pt.Y <= rect.Bottom)
            {
                return (pt.Y <= ptcenter.Y) ? "a8" : "a7";
            }

            var rate = rect.Height / rect.Width;

            // 四隅向（注意：点不在四边延长线上）
            // 点在矩形左上角
            if (pt.X < rect.Left && pt.Y < rect.Top)
            {
                return (Math.Abs(pt.Y - rect.Top) / Math.Abs(pt.X - rect.Left) > rate) ? "aa" : "ah";
            }

            // 点在矩形右上角
            if (pt.X > rect.Right && pt.Y < rect.Top)
            {
                return (Math.Abs(pt.Y - rect.Top) / Math.Abs(pt.X - rect.Right) > rate) ? "ab" : "ac";
            }

            // 点在矩形右下角
            if (pt.X > rect.Right && pt.Y > rect.Bottom)
            {
                return (Math.Abs(pt.Y - rect.Bottom) / Math.Abs(pt.X - rect.Right) > rate) ? "ae" : "ad";
            }

            // 点在矩形左下角
            if (pt.X < rect.Left && pt.Y > rect.Bottom)
            {
                return (Math.Abs(pt.Y - rect.Bottom) / Math.Abs(pt.X - rect.Left) > rate) ? "af" : "ag";
            }

            return "unknown";
        }
    }
}
